import random
from django import http
from django.shortcuts import render
from django.http import HttpResponse
from django.views import View

from apps.users.models import User
from apps.verifications.utils import generate_eccess_token, check_access_token
from libs.captcha.captcha import captcha
from django_redis import get_redis_connection

from libs.yuntongxun.sms import CCP
from .constants import IMAGE_CODE_EXPIRE_TIME, SMS_CODE_REDIS_EXPIRES, SEND_SMS_CODE_INTERVAL
import logging

logger = logging.getLogger('django')
from utils.response_code import RETCODE
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from meiduo_mall import settings
from itsdangerous import BadData


class ImageCodeView(View):
    def get(self, request, uuid):
        # code 是图片验证码   image是图片验证码二进制 name现在用不到
        name, code, image = captcha.generate_captcha()
        # 保存图片验证码
        redis_coon = get_redis_connection('code')
        # 写入redis图形验证码有有效期, 为了增加代码的可读性, 有效期使用常量在constants.py中
        redis_coon.setex('img:%s' % uuid, IMAGE_CODE_EXPIRE_TIME, code)
        # 返回数据需要设置图片格式 语法大类/小类 属于MIME
        return http.HttpResponse(image, content_type='image/jpeg')


'''
短信验证码业务实现逻辑

前端逻辑
1. 得到用户的手机号, 图片验证码, uuid
2. 得到用户输入的短信验证码


后端逻辑
1. 获取前端得到的数据, 根据uuid对redis中的图形验证码, 手机号进行对比验证
2. 验证没有问题以后, 生成短信验证码, 发送给用户
3. 获取前端的短信验证码, 与后端生成的验证码进行对比
4. 如果验证没有问题, 成功注册

请求方式和路径
GET     路径参数和查询参数结合 /sms_codes/(?P<mobile>1[3-9]\d{9})/
'''


class SmsCodeView(View):
    def get(self, request, mobile):
        # 1.获取前端得到的数据, 根据uuid对redis中的图形验证码, 手机号进行对比验证
        # image_code 在这里是前端获取的图片验证码
        image_code_client = request.GET.get('image_code')
        # image_code_id 在这里是图片验证码的uuid
        uuid = request.GET.get('image_code_id')

        # 验证数据是否缺少
        if not all([image_code_client, uuid]):
            return http.HttpResponseBadRequest('缺少必要参数')

        # 操作外界资源是, 需要进行异常捕获
        try:
            # 创建连接到redis的对象
            redis_conn = get_redis_connection('code')
            # 提取已经保存图形验证码
            image_code_server = redis_conn.get('img:%s' % uuid)
            # 判断保存的图片是否过期或不存在
            if image_code_server is None:
                return http.HttpResponseBadRequest('图片已过期')

            # 对比图形验证码  redis中的图形验证码是二进制大写数据, 需要解码转小写, 用户输入的也需要转小写
            if image_code_client.lower() != image_code_server.decode().lower():
                return http.HttpResponseBadRequest('验证码输入错误')

            # 删除对比后图片验证码, 防止恶意测试
            redis_conn.delete('img:%s' % uuid)

            # 获取标记, 验证标记
            send_flag = redis_conn.get('send_flag_%s' % mobile)
            if send_flag:
                return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '发送短信过于频繁'})



        except Exception as e:
            logger.error(e)
            return http.HttpResponseBadRequest('redis连接错误')

        # 2.验证没有问题以后, 生成短信验证码, 发送给用户
        sms_code = '%06d' % random.randint(0, 999999)

        # 保存短信验证码
        # redis_conn.setex('sms_%s' % mobile, SMS_CODE_REDIS_EXPIRES, sms_code)
        # 重新写入send_flag  120秒内不可以重复发送短信
        # redis_conn.setex('send_flag_%s' % mobile, SEND_SMS_CODE_INTERVAL, 1)

        # 使用管道连接redis
        pl = redis_conn.pipeline()
        # 将Redis请求添加到队列
        pl.setex('sms_%s' % mobile, SMS_CODE_REDIS_EXPIRES, sms_code)
        pl.setex('send_flag_%s' % mobile, SEND_SMS_CODE_INTERVAL, 1)
        # 执行请求
        pl.execute()

        # 发送短信验证码
        # CCP().send_template_sms(mobile, [sms_code, 5], 1)

        from celery_tasks.sms.tasks import send_sms_code
        # delay 参数同 任务参数
        send_sms_code.delay(mobile, sms_code)

        # 4.如果验证没有问题, 成功注册
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})



# 实现找回密码中验证用户名和图形验证码
class UsernameCodeView(View):
    def get(self, request, username):
        # 获取图形验证码
        image_code = request.GET.get('text')
        # 获取图片验证码的uuid
        uuid = request.GET.get('image_code_id')

        # 验证数据是否缺少
        if not all([image_code, uuid]):
            return http.HttpResponseBadRequest('缺少必要参数')

        try:
            # 连接redis, 验证图形验证码
            redis_conn = get_redis_connection('code')
            redis_image_code = redis_conn.get('img:%s' % uuid)
            # 判断验证码是否存在
            if redis_image_code is None:
                return http.HttpResponseBadRequest('图片验证码有误')
            # 对比后删除验证码
            redis_conn.delete('img:%s' % uuid)

            if image_code.lower() !=  redis_image_code.decode().lower():
                return http.HttpResponseBadRequest('验证码错误')
        except Exception as e:
            logger.error(e)
            return http.HttpResponseBadRequest('连接redis有误')

        # 验证用户名
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            return http.HttpResponseBadRequest('用户不存在')

        # 用户名和图形验证码都正确, 组织返回对用户id和用户手机号加密的数据
        # user_dict会传给下一步做手机号验证
        user_dict = {'user_id': user.id, 'mobile': user.mobile}
        access_token = generate_eccess_token(user_dict)

        # 返回的请求体中夹带了该用户的手机号,和上面加密的数据
        return http.JsonResponse({'mobile': user.mobile, 'access_token': access_token})



# 实现找回密码中验证手机号和短信验证码
class MobileSmsView(View):
    def get(self, request):
        # 获取路由中的token
        access_token = request.GET.get('access_token')
        user_dict = check_access_token(access_token)

        # 对user_dict进行判断
        if user_dict is None:
            return http.HttpResponseBadRequest('参数有误')

        # 对手机号判断
        mobile = user_dict['mobile']
        try:
            User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            return http.HttpResponseBadRequest('手机号不存在')

        # 2.验证没有问题以后, 生成短信验证码, 发送给用户
        sms_code = '%06d' % random.randint(0, 999999)

        # 连接redis, 发送验证码
        redis_conn = get_redis_connection('code')
        pl = redis_conn.pipeline()
        # 将Redis请求添加到队列
        pl.setex('sms_%s' % mobile, SMS_CODE_REDIS_EXPIRES, sms_code)
        pl.setex('send_flag_%s' % mobile, SEND_SMS_CODE_INTERVAL, 1)
        # 执行请求
        pl.execute()

        from celery_tasks.sms.tasks import send_sms_code
        # delay 参数同 任务参数  使用celery发送短信验证码
        send_sms_code.delay(mobile, sms_code)

        # 4.如果验证没有问题, 成功注册
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})


# 处理短信验证码校验
class PwdChangeView(View):
    def get(self, request, username):
        # 获取前端的短信验证码, 与后端生成的验证码进行对比
        sms_code_client = request.GET.get('sms_code')

        # 验证用户名
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            return http.HttpResponseBadRequest('用户不存在')

        # 连接redis, 查询redis内的短信验证码
        redis_conn = get_redis_connection('code')
        sms_code_redis = redis_conn.get('sms_%s' % user.mobile)

        # 对redis中的短信进行判断
        if sms_code_redis is None:
            return http.HttpResponseBadRequest('短信验证码已过期')

        # 对输入的短信验证码进行判断
        if sms_code_redis.decode() != sms_code_client:
            return http.HttpResponseBadRequest('短信验证码输入有误')

        # 验证没有问题后删除redis中的短信验证码
        redis_conn.delete('sms_%s' % user.mobile)

        # 重新加密token
        user_dict = {'user_id': user.id, 'mobile': user.mobile}
        access_token = generate_eccess_token(user_dict)

        return http.JsonResponse({'user_id': user.id, 'access_token': access_token})